/*
reflect

反射可大大提高程序的灵活性，使得interface{}有更大的发挥余地
反射使用Type()和ValueOf函数从接口中获取目标对象信息
反射会将名字字段作为独立字段（匿名字段本质）
想要利用反射修改对象状态，前提是interface.data是settable，
即pointer-interface
通过反射可以“动态”调用方法
*/
package main

import (
	"fmt"
	"reflect"
)

type User struct {
	Id   int
	Name string
	Age  int
}

func (u User) Hello(name string) {
	fmt.Println("Hello", name, "my name is", u.Name)
}

func Info(o interface{}) {
	t := reflect.TypeOf(o) //获取反射类型
	fmt.Println("Type:", t.Name())

	if k := t.Kind(); k != reflect.Struct { //判断传入类型的kind
		fmt.Println("XX")
		return
	}

	v := reflect.ValueOf(o) //获取反射值
	fmt.Println("Fields:")
	numField := t.NumField()
	for i := 0; i < numField; i++ {
		f := t.Field(i)
		val := v.Field(i).Interface()
		fmt.Printf("%6s:%v =%v\n", f.Name, f.Type, val)
	}

	fmt.Println("Methods:")
	numMethod := t.NumMethod()
	for i := 0; i < numMethod; i++ {
		m := t.Method(i)
		fmt.Printf("%6s:%v\n", m.Name, m.Type)
	}

}

type Manager struct {
	User
	title string
}

func Set(o interface{}) {
	v := reflect.ValueOf(o)
	if v.Kind() != reflect.Ptr || !v.Elem().CanSet() {
		fmt.Println("XXX")
		return
	}
	v = v.Elem()
	f := v.FieldByName("Name") //大小写敏感
	if !f.IsValid() {          //判断反射返回知否可用
		fmt.Println("BAD")
		return
	}
	if f.Kind() == reflect.String {
		f.SetString("BYEBYE")
	}
}

type ClassroomWork struct {
	Name string
}

func (c ClassroomWork) PrintName(callerName string) {
	fmt.Println("ClassroomWork.name :", c.Name, "callerName:", callerName)
}

func main() {
	fmt.Println("-----------------获取反射信息------------------")
	a := User{1, "OK", 12}
	Info(a)
	Info(&a) //传入非struct的指针类型
	fmt.Println("-----------------获取嵌入类型的反射信息------------------")
	b := Manager{User: User{1, "OK", 12}, title: "123"}
	b1 := reflect.TypeOf(b)
	fmt.Printf("%#v\n", b1.Field(0))
	fmt.Printf("%#v\n", b1.FieldByIndex([]int{0, 1}))
	fmt.Println("----------------反射修改int变量值-------------------")
	c := 123
	//修改需要传指针，用Elem()调用SetXX方法
	c1 := reflect.ValueOf(&c)
	c1.Elem().SetInt(456)
	fmt.Println(c)
	fmt.Println("---------------反射修改成员值--------------------")
	d := User{1, "ok", 12}
	Set(&d)
	fmt.Println(d)
	fmt.Println("---------------反射动态调用方法--------------------")
	e := User{1, "ok", 12}
	e1 := reflect.ValueOf(e)
	e2 := e1.MethodByName("Hello")
	e3 := []reflect.Value{reflect.ValueOf("joe")}
	e2.Call(e3)

	fmt.Println("--------------classroom work---------------------")
	fmt.Println("--------------定义结构，通过反射来打印信息，并调用方法---------------------")
	f := ClassroomWork{"workName"}
	f1 := reflect.TypeOf(f)
	f2 := reflect.ValueOf(f)
	fmt.Println("Type:", f1.Name())
	fnumField := f1.NumField()
	for i := 0; i < fnumField; i++ {
		field := f1.Field(i)
		val := f2.Field(i).String()
		fmt.Printf("%s:%v=%v\n", field.Name, field.Type, val)
	}
	fnumMethod := f1.NumMethod()
	for i := 0; i < fnumMethod; i++ {
		field := f1.Method(i)
		fmt.Printf("%s:%v\n", field.Name, field.Type)
	}
	fPrintName := f2.MethodByName("PrintName")
	fargs := []reflect.Value{reflect.ValueOf("caller")}
	if !fPrintName.IsValid() {
		fmt.Println("fail to get reflect func PrintName!")
	} else {
		fPrintName.Call(fargs)
	}
}
